{
 "cells": [
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "# 9 Combining and Splitting Value（值的组合与分割）"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 原文与翻译\n",
    "\n",
    "> Although it would be possible to handle coins individually, it would be unwieldy to make a separate transaction for every cent in a transfer. To allow value to be split and combined, transactions contain multiple inputs and outputs. Normally there will be either a single input from a larger previous transaction or multiple inputs combining smaller amounts, and at most two outputs: one for the payment, and one returning the change, if any, back to the sender.\n",
    "> \n",
    "> 尽管逐个地处理硬币是可能的，但为每分钱设置一个单独的记录是很笨拙的。为了允许值的分割与合并，交易记录包含多个输入和输出。一般情况下，要么是一个单独的来自于一个相对大的之前的交易的输入，要么是很多个输入来自于更小金额的组合；与此同时，最多有两个输出：一个是支付（指向收款方），如果必要的话，另外一个是找零（指向发款方）。\n",
    "> \n",
    "> ![tx_ios](pics/tx_ios.svg)\n",
    "> \n",
    "> It should be noted that fan-out, where a transaction depends on several transactions, and those transactions depend on many more, is not a problem here. There is never the need to extract a complete standalone copy of a transaction's history.\n",
    "> \n",
    "> 值得注意的是，“扇出”在这里并不是问题 —— 所谓“扇出”，就是指一笔交易依赖于数笔交易，且这些交易又依赖于更多笔交易。从来就没有必要去提取任何一笔交易的完整独立的历史拷贝。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 比特币的本质——历史记录不可篡改的账本\n",
    "\n",
    "如果要向完全不了解区块链或者比特币的人用一句话介绍比特币，那么就是这一句——\n",
    "比特币的本质是一本**「历史记录不可篡改的账本」**。\n",
    "\n",
    "这个账本即由无数的上图所示的交易所组成。\n",
    "\n",
    "![txs.jpeg](pics/txs.jpeg)\n",
    "\n",
    "值得注意的是，这个账本里没有传统银行系统所谓的「账户」。\n",
    "- 要查一个地址的所有交易记录，那么只要检索所有的历史 Output，找出相关交易即可。\n",
    "- 要发送一笔交易，那么只要在「UTXO 池」中找到 Output，用私钥构造交易即可。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 通过 bitcoinlib 库实现比特币交易全过程\n",
    "\n",
    "【Python 知识点】 点击链接跳转至《自学是门手艺》相应知识点\n",
    "- [函数](https://github.com/selfteaching/the-craft-of-selfteaching/blob/master/Part.2.D.1-args.ipynb)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [],
   "source": [
    "import hashlib\n",
    "from bitcoin import SelectParams\n",
    "from bitcoin.core import b2x, lx, COIN, COutPoint, CMutableTxOut, CMutableTxIn, CMutableTransaction, Hash160\n",
    "from bitcoin.core.script import CScript, OP_DUP, OP_HASH160, OP_EQUALVERIFY, OP_CHECKSIG, SignatureHash, SIGHASH_ALL\n",
    "from bitcoin.core.scripteval import VerifyScript, SCRIPT_VERIFY_P2SH\n",
    "from bitcoin.wallet import CBitcoinAddress, CBitcoinSecret"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 2,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Script Hash 地址: 323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp\n"
     ]
    }
   ],
   "source": [
    "# 通过私钥生成 p2sh 地址\n",
    "h = hashlib.sha256(b'correct horse battery staple').digest()\n",
    "secret_key = CBitcoinSecret.from_secret_bytes(h)\n",
    "txin_redeemScript = CScript([secret_key.pub, OP_CHECKSIG])\n",
    "txin_scriptPubKey = txin_redeemScript.to_p2sh_scriptPubKey()\n",
    "txin_p2sh_address = CBitcoinAddress.from_scriptPubKey(txin_scriptPubKey)\n",
    "\n",
    "print('Script Hash 地址:', str(txin_p2sh_address))"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 3,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "Transaction In: \n",
      " CTxIn(COutPoint(lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae'), 0), CScript([]), 0xffffffff)\n",
      "Transaction Out: \n",
      " CTxOut(0.0000001*COIN, CScript([OP_HASH160, x('03f3814939dfe1b20dffc8965d547950a1493a8a'), OP_EQUAL]))\n",
      "Transaction: \n",
      " CTransaction([CTxIn(COutPoint(lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae'), 0), CScript([]), 0xffffffff)], [CTxOut(0.0000001*COIN, CScript([OP_HASH160, x('03f3814939dfe1b20dffc8965d547950a1493a8a'), OP_EQUAL]))], 0, 1, CTxWitness(CTxInWitness(CScriptWitness())))\n"
     ]
    }
   ],
   "source": [
    "# 构造未签名交易\n",
    "txid = lx('bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae')\n",
    "vout = 0\n",
    "txin = CMutableTxIn(COutPoint(txid, vout))\n",
    "print(\"Transaction In: \\n\", txin)\n",
    "txout = CMutableTxOut(10, CBitcoinAddress('323uf9MgLaSn9T7vDaK1cGAZ2qpvYUuqSp').to_scriptPubKey())\n",
    "print(\"Transaction Out: \\n\", txout)\n",
    "tx = CMutableTransaction([txin], [txout])\n",
    "print(\"Transaction: \\n\", tx)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 4,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 生成签名\n",
    "sighash = SignatureHash(txin_redeemScript, tx, 0, SIGHASH_ALL)\n",
    "sig = secret_key.sign(sighash) + bytes([SIGHASH_ALL])"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 5,
   "metadata": {},
   "outputs": [],
   "source": [
    "# 将签名附加到 txin 中\n",
    "txin.scriptSig = CScript([sig, txin_redeemScript])"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "知识回顾：\n",
    "\n",
    "> Output 包含两个部分 —— 一个表明表示这个 Output 里有多少比特币的数字与一个公钥脚本。打个比方，就是一个蓄水池与一个水龙头，水龙头需要用一把钥匙打开（签名脚本）。如果钥匙匹配，蓄水池打开，水就变成接收者的了。\n",
    "\n",
    "现在我们所做的，即是把 txid 为 bff785da9f8169f49be92fa95e31f0890c385bfb1bd24d6b94d7900057c617ae 的交易的 output，拿来作为新的交易的 input，并通过「钥匙」证明我们能合法的打开这个「水龙头」。"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 6,
   "metadata": {},
   "outputs": [
    {
     "name": "stdout",
     "output_type": "stream",
     "text": [
      "0100000001ae17c6570090d7946b4dd21bfb5b380c89f0315ea92fe99bf469819fda85f7bf000000006c47304402204d923c5b91a7fb55d32e707d3824a2fd4e8972b7fe89b87d8582aaa69f301060022077d58167312eeaab92ca377250072d59e0840de5df53b5fa28f0a12a15c3ba010123210378d430274f8c5ec1321338151e9f27f4c676a008bdf8638d07c0b6be9ab35c71acffffffff010a0000000000000017a91403f3814939dfe1b20dffc8965d547950a1493a8a8700000000\n"
     ]
    }
   ],
   "source": [
    "print(b2x(tx.serialize()))"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "上面的这一串十六进制码，就是我们在比特币网络中实际发送的交易了。"
   ]
  },
  {
   "cell_type": "markdown",
   "metadata": {},
   "source": [
    "## 延伸阅读\n",
    "\n",
    "比特币的交易结构：\n",
    "\n",
    "![tx_struct](pics/tx_struct.png)"
   ]
  },
  {
   "cell_type": "code",
   "execution_count": 1,
   "metadata": {},
   "outputs": [
    {
     "data": {
      "text/html": [
       "\n",
       "        <iframe\n",
       "            width=\"100%\"\n",
       "            height=\"4000\"\n",
       "            src=\"others/parse_bitcoin_data.html\"\n",
       "            frameborder=\"0\"\n",
       "            allowfullscreen\n",
       "        ></iframe>\n",
       "        "
      ],
      "text/plain": [
       "<IPython.lib.display.IFrame at 0x105a1ff90>"
      ]
     },
     "execution_count": 1,
     "metadata": {},
     "output_type": "execute_result"
    }
   ],
   "source": [
    "from IPython.display import IFrame\n",
    "\n",
    "IFrame('others/parse_bitcoin_data.html', width='100%', height='4000')"
   ]
  }
 ],
 "metadata": {
  "kernelspec": {
   "display_name": "Python 3",
   "language": "python",
   "name": "python3"
  },
  "language_info": {
   "codemirror_mode": {
    "name": "ipython",
    "version": 3
   },
   "file_extension": ".py",
   "mimetype": "text/x-python",
   "name": "python",
   "nbconvert_exporter": "python",
   "pygments_lexer": "ipython3",
   "version": "3.7.4"
  }
 },
 "nbformat": 4,
 "nbformat_minor": 2
}
